#include <iostream>
#include <set>

#include "../common/baseTypes.h"
#include "../common/ioUtils.h"
#include "../common/ProblemsContainerBase.h"

#include "../root/root.h"
#include "../giorgio.ugazio/giorgio.ugazio.h"
#include "../martino.giovanelli/martino.giovanelli.h"
#include "../Peranzoni/Peranzoni.h"
#include "../macinteo/macinteo.h"
#include "../filippo.fantini/filippo.fantini.h"
#include "../andrea.mugnaini/andrea.mugnaini.h"
#include "../jacopo.musso/jacopo.musso.h"
#include "../alberto.destro/alberto.destro.h"
#include "../luca.giona/luca.giona.h"
#include "../mauro.bordin/mauro.bordin.h"
#include "../giorgio.tognali/giorgio.tognali.h"
#include "../daniele.crupi/daniele.crupi.h"
#include "../giovanni.peroni/giovanni.peroni.h"
//#include "../pinco.pallino/pinco.pallino.h"

using namespace std;

bool Init(ProblemsContainer& o_users, Register& o_register)
{
	o_users.clear();
	o_users.push_back(new Root);
	o_users.push_back(new GiorgioUgazio);
	o_users.push_back(new MartinoGiovanelli);
	o_users.push_back(new Peranzoni);
	o_users.push_back(new Macinteo);
	o_users.push_back(new FilippoFantini);
	o_users.push_back(new AndreaMugnaini);
	o_users.push_back(new JacopoMusso);
	o_users.push_back(new AlbertoDestro);
	o_users.push_back(new LucaGiona);
	o_users.push_back(new MauroBordin);
	o_users.push_back(new GiorgioTognali);
	o_users.push_back(new DanieleCrupi);
	//o_users.push_back(new GiovanniPeroni);
	//o_users.push_back(new PincoPallino);

	o_register.clear();
	for(ProblemsContainer::iterator it= o_users.begin(); it!= o_users.end(); ++it)
		if(!((*it)->Init(o_register)))
			return false;
	
	return true;
}

bool SolveProblems(const Register& i_register)
{
	UInt currentProblem = 0;
	Register::const_iterator currentRegisterEntry = i_register.begin();

	while(currentRegisterEntry!= i_register.end())
	{
		const UInt nextProblem = currentRegisterEntry->first;
		const ProblemsContainerBase* currentUser = currentRegisterEntry->second;

		Timer timer;
		const RetType answer = currentUser->SolveProblem(nextProblem);
		const UInt64 timeElapsed = timer.TimeElapsedMicroSec();

		if (currentProblem != nextProblem)
		{
			cout << endl << "Problem: " << nextProblem << endl;
			currentProblem = nextProblem;
		}

		cout << "User: " << currentUser->GetName();
		cout << " - Answer: " << answer;
		cout << " - Time Elapsed (microsec): " << timeElapsed << endl;
		
		++currentRegisterEntry;
	}

	return true;
}

UInt GetNumFacedProblems(const Register& i_register)
{
	set<UInt> theSet;
	for(Register::const_iterator it= i_register.begin(); it!= i_register.end(); ++it)
		theSet.insert(it->first);
	return theSet.size();
}

bool Shutdown(ProblemsContainer& o_users)
{
	for(ProblemsContainer::iterator it= o_users.begin(); it!= o_users.end(); ++it)
	{
		if(!((*it)->Shutdown()))
			return false;
		delete *it;
	}

	o_users.clear();
	return true;
}

int main()
{
	ProblemsContainer theUsers;
	Register theRegister;

	//initialization
	cout << endl << "Start Initialization" << endl << endl;
	if(!Init(theUsers, theRegister))
	{
		cout << "Error in Initialization, press a key to abort (or exit in 5 seconds...)" << endl;
		PressKey(5);
		return 1;
	}else{
		cout << "Registered Users: " << theUsers.size() << endl;
		cout << "Faced Problems: " << GetNumFacedProblems(theRegister) << endl;
		cout << "Registered Solutions: " << theRegister.size() << endl << endl;
		cout << "End Initialization" << endl << endl;
	}

	//problems
	cout << endl << "Start Problem solving" << endl;
	if(!SolveProblems(theRegister))
	{
		cout << "Error in Problem Solving, press a key to abort (or exit in 5 seconds...)" << endl;
		PressKey(5);
		return 1;
	}else{
		cout << endl << "End Problem Solving" << endl << endl;
	}

	//shutdown
	cout << endl << "Start Shutdown" << endl << endl;
	if(!Shutdown(theUsers))
	{
		cout << "Error in Shutdown, press a key to abort (or exit in 5 seconds...)" << endl;
		PressKey(5);
		return 1;
	}else{
		cout << "End Shutdown" << endl << endl;
	}

	cout << "End of execution, press a key to exit!"<<endl;
	PressKey();
	return 0;
}